/*------------------- COPYRIGHT AND TRADEMARK INFORMATION ------------------+
 |                                                                          |
 |    VoCon (R) 3200 Embedded Development System                            |
 |                                                                          |
 |    (c) 2009 Nuance Communications, Inc. All rights reserved.             |
 |                                                                          |
 |    Nuance, the Nuance logo and VoCon are trademarks and/or registered    |
 |    trademarks of Nuance Communications, Inc. or its affiliates in the    |
 |    United States and/or other countries.                                 |
 |                                                                          |
 +--------------------------------------------------------------------------*/

/* -------------------------------------------------------------------------+
 |   HEADER (INCLUDE) SECTION                                               |
 + -------------------------------------------------------------------------*/

//#include "stdafx.h"
#include <string>
#include "a2s_asr2sem.h"
#include "string.h"
/* -------------------------------------------------------------------------+
 | ASR2SEM                                                                  |
 |                                                                          |
 + -------------------------------------------------------------------------*/




/* -------------------------------------------------------------------------+
 | __Asr2Sem_Calloc                                                         |
 |                                                                          |
 | Calloc wrapper with error checking.                                      |
 | Makes sure that zero-length arrays are in fact a NULL pointer.           |
 + -------------------------------------------------------------------------*/
static void* __Asr2Sem_Calloc2(PASR2SEM2 self, unsigned long nElements, unsigned long size, LH_ERROR* pErr)
{
  void* pMem = NULL;
  if(pErr==NULL) goto end;

  if(self==NULL){
    *pErr = LH_E_ARG;
    goto end;
  }

  if(nElements == 0){
    goto end;
  }

  pMem = self->heapInterface.pcalloc(self->pHeapInst, nElements, size);
  if(! pMem){
    *pErr = LH_E_RESOURCE;
    goto end;
  }

end:
  return pMem;
}
/* End of __Asr2Sem_Calloc */


/* -------------------------------------------------------------------------+
 | __Asr2Sem_Free                                                           |
 |                                                                          |
 | Free wrapper.                                                            |
 + -------------------------------------------------------------------------*/
static void __Asr2Sem_Free2(PASR2SEM2 self, void* pMem)
{
  self->heapInterface.pfree(self->pHeapInst, pMem);
}
/* End of __Asr2Sem_Free. */


/* -------------------------------------------------------------------------+
 | CALLBACK FUNCTION IMPLEMENTATIONS                                        |
 |                                                                          |
 | These functions provide the implementation of the callback functions     |
 | needed by the LH_SEMPROC_NBESTRESULT_INTERFACE.                          |
 + -------------------------------------------------------------------------*/
static int Asr2Sem_GetNbrHypotheses2(void* self, unsigned long* pNbrHypotheses)
{
  LH_ERROR err = LH_OK;
  PASR2SEM2 pAsr2Sem = (PASR2SEM2)self;

  if((self==NULL)||(pNbrHypotheses==NULL)) {
    err = LH_E_ARG;
    goto end;
  }

  *pNbrHypotheses = pAsr2Sem->nbrHypos;

end:
  return (int)err;
}

static int Asr2Sem_FetchHypothesis2(void* self, unsigned long hypothesisNbr,
                                   LH_SEMPROC_NBESTRESULT_HYPOTHESIS** ppHypothesis)
{
  LH_ERROR err = LH_OK;
  PASR2SEM2 pAsr2Sem = (PASR2SEM2)self;

  if((self==NULL)||(ppHypothesis==NULL)) {
    err = LH_E_ARG;
    goto end;
  }

  if(hypothesisNbr < pAsr2Sem->nbrHypos) {
    *ppHypothesis = &(pAsr2Sem->aHypos[hypothesisNbr]);
  }
  else{
    err = LH_E_ARG;
    goto end;
  }

end:
  return (int)err;
}

static int Asr2Sem_ReturnHypothesis2(void* self, LH_SEMPROC_NBESTRESULT_HYPOTHESIS* pHypothesis)
{
  LH_ERROR err = LH_OK;

  if((self==NULL)||(pHypothesis==NULL)) {
    err = LH_E_ARG;
    goto end;
  }

end:
  return err;
}
/* END OF CALLBACK FUNCTION IMPLEMENTATIONS. */

/* -------------------------------------------------------------------------+
 | __Asr2Sem_NormalizeSlotName                                              |
 |                                                                          |
 | Normalize the slot names coming out of the ASR result objects to the     |
 | format expected by the SEM: starts and ends with '<' resp. '>', and      |
 | only the part after the last '#' is used.                                |
 | Allocates memory for the returned string.                                |
 + -------------------------------------------------------------------------*/
static LH_ERROR __Asr2Sem_NormalizeSlotName(PASR2SEM2 hAsr2Sem, const char* szSlotName, char** pszNormalizedSlotName)
{
  LH_ERROR err = LH_OK;
  size_t slotStringLen;
  size_t origIdx;
  size_t newIdx;
  
  slotStringLen = strlen(szSlotName);
  /* Returned string can be maximally slotStringLen + 3 long. */
  *pszNormalizedSlotName = (char*) __Asr2Sem_Calloc2(hAsr2Sem, slotStringLen + 3, sizeof(char), &err);
  if (LH_OK != err) goto end;
  
  /* Loop over the original and new slot name, copying character by character as appropriate */
  newIdx = 0;
  for( origIdx=0; origIdx <= slotStringLen; origIdx++)
  {
    /* make sure the new name starts with '<' */
    if ((newIdx==0)&&(szSlotName[origIdx] != '<'))
    {
      (*pszNormalizedSlotName)[newIdx] = '<';
      newIdx ++;
    }
    /* When encountering a '#' in the original slot name, start at 0 for the new slot name */
    if(szSlotName[origIdx] == '#')
    {
      newIdx = 0;      
    }
    else{
      /* make sure the new name end in '>' */
      if((origIdx == slotStringLen)&&((*pszNormalizedSlotName)[newIdx-1]!='>')){
        (*pszNormalizedSlotName)[newIdx] = '>';
        newIdx ++;
      }
      (*pszNormalizedSlotName)[newIdx] = szSlotName[origIdx];
      newIdx ++;
    }
  }
  
  end:
    return err;
  }
  /* End of __Asr2Sem_NormalizeSlotName. */



/* -------------------------------------------------------------------------+
 | __Asr2Sem_FillWordClasses                                                |
 |                                                                          |
 | Fill out the word classes section of LH_SEMPROC_NBESTRESULT_WORD.        |
 | Function will first check if the given slot tag exists in the sem word   |
 | classes list. If it exists corresponding class ID will be set properly.  |
 + -------------------------------------------------------------------------*/
static LH_ERROR __Asr2Sem_FillWordClasses2 (PASR2SEM2 hAsr2Sem,
                                           LH_SEMPROC_NBESTRESULT_WORD* pSemWord,
                                           LH_HYPOTHESIS_WORD* pAsrWord,
                                           size_t nbrWordsInSlot) {
  LH_ERROR err = LH_OK;
  size_t idx = 0;
  LH_SEMPROC_NBESTRESULT_CLASS* pClass = NULL;
  unsigned long nbrClasses = 0;
  char* szNormalizedSlotName = NULL;
  
  err = __Asr2Sem_NormalizeSlotName(hAsr2Sem, pAsrWord->szWord, &szNormalizedSlotName); 
  if(LH_OK != err) goto end;
  
  for (idx=0;idx<hAsr2Sem->nbrClasses;idx++) {
    if (!strcmp(szNormalizedSlotName,hAsr2Sem->pWordClasses[idx].szName)) {
      nbrClasses++;
      pClass = (LH_SEMPROC_NBESTRESULT_CLASS *)__Asr2Sem_Calloc2(hAsr2Sem,nbrClasses,sizeof(LH_SEMPROC_NBESTRESULT_CLASS),&err);
      if (LH_OK != err) goto end;
      pClass->nbrSpannedWords = nbrWordsInSlot;
      pClass->ID =  hAsr2Sem->pWordClasses[idx].ID;
      break;
    }
  }
  pSemWord->pClasses = pClass;
  pSemWord->nbrClasses = nbrClasses;
  __Asr2Sem_Free2(hAsr2Sem, szNormalizedSlotName);
end:
  return err;
}
/* -------------------------------------------------------------------------+
 | __Asr2Sem_FillWordFeatures                                               |
 |                                                                          |
 | Fill out all the features defined in the buffer (listed in aFeatures) for|
 | the LH_SEMPROC_NBESTRESULT_WORD based on the LH_HYPOTHESIS_WORD.         |
 + -------------------------------------------------------------------------*/
static LH_ERROR __Asr2Sem_FillWordFeatures2(PASR2SEM2 hAsr2Sem,
                                           LH_SEMPROC_NBESTRESULT_WORD* pSemWord,
                                           LH_HYPOTHESIS_WORD* pAsrWord)
{
  LH_ERROR err = LH_OK;
  unsigned long featNb;             /* Loop counter for the features. */
  unsigned long nbrFeatsToSet = 0;  /* Number of configured word features (both mandatory and optional). */

  /* Check how many features are needed. */
  for(featNb=0;featNb<hAsr2Sem->nbrFeatures;featNb++){
    if(hAsr2Sem->aFeatures[featNb].properties & LH_SEMPROC_FEATURE_FLAG_WORD){
      nbrFeatsToSet += 1;
    }
  }

  /* Allocate the features array in the Sem word and set to 0's. */
  pSemWord->pFeatures = (LH_SEMPROC_NBESTRESULT_FEATURE*) __Asr2Sem_Calloc2(hAsr2Sem, nbrFeatsToSet, sizeof(LH_SEMPROC_NBESTRESULT_FEATURE), &err);
  if (LH_OK != err) goto end;

  /* Loop over the configured features. */
  pSemWord->nbrFeatures = 0;
  for(featNb=0;featNb<hAsr2Sem->nbrFeatures;featNb++){
    char*         szFeatName = hAsr2Sem->aFeatures[featNb].szName;
    unsigned long featID     = hAsr2Sem->aFeatures[featNb].ID;
    signed long   featVal    = 0;

    if(hAsr2Sem->aFeatures[featNb].properties & LH_SEMPROC_FEATURE_FLAG_WORD){
      /*
       * This block compares possibly configured features in the SEM buffer
       * with a standard set of feature names currently supported by the VoCon result
       * object and the asr2sem mapping.
       * Adapters for specialized SEM buffers with additional features, should add
       * the names of the features here, as well as create a mechanism to provide the
       * values for these features.
       */
      if (strcmp(szFeatName, "confidence") == 0){
        featVal = (signed long) pAsrWord->conf;
        pSemWord->pFeatures[pSemWord->nbrFeatures].ID = featID;
        pSemWord->pFeatures[pSemWord->nbrFeatures].value = featVal;
        pSemWord->nbrFeatures += 1;
      }
      else if (strcmp(szFeatName, "score") == 0){
        featVal = (signed long) pAsrWord->score;
        pSemWord->pFeatures[pSemWord->nbrFeatures].ID = featID;
        pSemWord->pFeatures[pSemWord->nbrFeatures].value = featVal;
        pSemWord->nbrFeatures += 1;
      }
      else if (strcmp(szFeatName, "lmScore") == 0){
        featVal = (signed long) pAsrWord->lmScore;
        pSemWord->pFeatures[pSemWord->nbrFeatures].ID = featID;
        pSemWord->pFeatures[pSemWord->nbrFeatures].value = featVal;
        pSemWord->nbrFeatures += 1;
      }
      else{
        if(hAsr2Sem->aFeatures[featNb].properties & LH_SEMPROC_FEATURE_FLAG_MANDATORY){
          /*
           * This error occurs when the SEM buffer has a mandatory feature
           * not provided for by this standard asr2sem adapter.
           */
          err = LH_E_DATA;
          goto end;
        }
      }
    }
  }

end:
  return err;

}
/* End of __Asr2Sem_FillWordFeatures. */

/* -------------------------------------------------------------------------+
 | __Asr2Sem_FillHypoFeatures                                               |
 |                                                                          |
 | Fill out all the features defined in the buffer (hAsr2Sem->aFeatures) for|
 | the LH_SEMPROC_NBESTRESULT_HYPOTHESIS based on the LH_HYPOTHESIS.        |
 + -------------------------------------------------------------------------*/
static LH_ERROR __Asr2Sem_FillHypoFeatures2(PASR2SEM2 hAsr2Sem,
                                           LH_OBJECT hNBestResult,
                                           LH_SEMPROC_NBESTRESULT_HYPOTHESIS* pSemHyp,
                                           LH_HYPOTHESIS* pAsrHypothesis)
{
  LH_ERROR err = LH_OK;
  unsigned long featNb;
  unsigned long nbrFeatsToSet = 0;

  /* Check how many features are needed. (For allocation) */
  for(featNb=0;featNb<hAsr2Sem->nbrFeatures;featNb++){
    if(hAsr2Sem->aFeatures[featNb].properties & LH_SEMPROC_FEATURE_FLAG_HYPOTHESIS){
      nbrFeatsToSet += 1;
    }
  }

  /* Allocate the features array in the Sem hypothesis and set to 0's. */
  pSemHyp->pFeatures = (LH_SEMPROC_NBESTRESULT_FEATURE*) __Asr2Sem_Calloc2(hAsr2Sem, nbrFeatsToSet, sizeof(LH_SEMPROC_NBESTRESULT_FEATURE), &err);
  if (LH_OK != err) goto end;

  /* Loop over the configured features. */
  pSemHyp->nbrFeatures = 0;
  for(featNb=0;featNb<hAsr2Sem->nbrFeatures;featNb++){
    signed long featVal = 0;
    char* szFeatName = hAsr2Sem->aFeatures[featNb].szName;
    unsigned long featID = hAsr2Sem->aFeatures[featNb].ID;

    if(hAsr2Sem->aFeatures[featNb].properties & LH_SEMPROC_FEATURE_FLAG_HYPOTHESIS){
      /*
       * This block compares possibly configured features in the SEM buffer
       * with a standard set of feature names currently supported by the VoCon result
       * object and the asr2sem mapping.
       * Adapters for specialized SEM buffers with additional features, should add
       * the names of the features here, as well as create a mechanism to provide the
       * values for these features.
       */
      if (strcmp(szFeatName, "confidence") == 0){
        featVal = (signed long) pAsrHypothesis->conf;
        pSemHyp->pFeatures[pSemHyp->nbrFeatures].ID = featID;
        pSemHyp->pFeatures[pSemHyp->nbrFeatures].value = featVal;
        pSemHyp->nbrFeatures += 1;
      }
      else if (strcmp(szFeatName, "score") == 0){
        featVal = (signed long) pAsrHypothesis->score;
        pSemHyp->pFeatures[pSemHyp->nbrFeatures].ID = featID;
        pSemHyp->pFeatures[pSemHyp->nbrFeatures].value = featVal;
        pSemHyp->nbrFeatures += 1;
      }
      else if (strcmp(szFeatName, "lmScore") == 0){
        featVal = (signed long) pAsrHypothesis->lmScore;
        pSemHyp->pFeatures[pSemHyp->nbrFeatures].ID = featID;
        pSemHyp->pFeatures[pSemHyp->nbrFeatures].value = featVal;
        pSemHyp->nbrFeatures += 1;
      }
      else if (strcmp(szFeatName, "langModelType") == 0){
        LH_OBJECT hCtx = lh_GetNullObj();
        LH_BOOL hasInterface = LH_FALSE;

        err = lh_ResultBorrowSourceCtx(hNBestResult, &hCtx);
        if (LH_OK != err) goto end;

        /* Check the type of language model (SLM/WTP only) used in the context. */
        {
          err = lh_ObjHasInterface(hCtx, LH_IID_IBNFCTX, &hasInterface);
          if (err != LH_OK) goto end;

          if (hasInterface){
            featVal = (signed long) LH_IID_IBNFCTX;
          }
        }
        if (!hasInterface){
          err = lh_ObjHasInterface(hCtx, LH_IID_ISLMCTX, &hasInterface);
          if (err != LH_OK) goto end;

          if (hasInterface){
            featVal = (signed long) LH_IID_ISLMCTX;
          }
        }
        if (!hasInterface){
          err = lh_ObjHasInterface(hCtx, LH_IID_ITREECTX, &hasInterface);
          if (err != LH_OK) goto end;

          if (hasInterface){
            featVal = (signed long) LH_IID_ITREECTX;
          }
        }
        if (!hasInterface){
          err = lh_ObjHasInterface(hCtx, LH_IID_ICTXFSTDP, &hasInterface);
          if (err != LH_OK) goto end;

          if (hasInterface){
            /* langModelType should be IBNFCTX unless it is an FSTSLM context */
            char* szStartRule = pAsrHypothesis->szStartRule;
            featVal = (signed long) LH_IID_IBNFCTX;
            if (szStartRule) {
              if(!strncmp(szStartRule,"slm",3)||!strncmp(szStartRule,"SLM",3)) {
                featVal = (signed long) LH_IID_ISLMCTX; /* fallback to SLM case */
              }
            }
          }          
        }
        if (!hasInterface){
          if(hAsr2Sem->aFeatures[featNb].properties & LH_SEMPROC_FEATURE_FLAG_MANDATORY){
            err = LH_E_ARG;
            goto end;
          }
        }
        else{
          pSemHyp->pFeatures[pSemHyp->nbrFeatures].ID = featID;
          pSemHyp->pFeatures[pSemHyp->nbrFeatures].value = featVal;
          pSemHyp->nbrFeatures += 1;
        }
      }
      else{
        if(hAsr2Sem->aFeatures[featNb].properties & LH_SEMPROC_FEATURE_FLAG_MANDATORY){
          /*
           * This error occurs when the SEM buffer has a mandatory feature
           * not provided for by this standard asr2sem adapter.
           */
          err = LH_E_DATA;
          goto end;
        }
      }
    }
  }

end:
  return err;
}
/* End of __Asr2Sem_FillHypoFeatures. */

/* -------------------------------------------------------------------------+
 | __Asr2Sem_FreeHypotheses                                                 |
 |                                                                          |
 | Free all the memory allocated while creating the semproc input.          |
 + -------------------------------------------------------------------------*/
static LH_ERROR __Asr2Sem_FreeHypotheses2(PASR2SEM2 hAsr2Sem)
{
  LH_ERROR err = LH_OK;
  unsigned long hypoNb;

  if ((hAsr2Sem->nbrHypos > 0) && (hAsr2Sem->aHypos == NULL)){
    err = LH_E_ARG;
    goto end;
  }

  for(hypoNb=0; hypoNb<hAsr2Sem->nbrHypos; hypoNb++){
    LH_SEMPROC_NBESTRESULT_HYPOTHESIS* pSemHyp = &(hAsr2Sem->aHypos[hypoNb]);
    size_t                             wordNb;

    /* Free the words: features, classes and strings. */
    for(wordNb=0; wordNb<pSemHyp->nbrWords; wordNb++){
      __Asr2Sem_Free2(hAsr2Sem, pSemHyp->pWords[wordNb].pFeatures);
      __Asr2Sem_Free2(hAsr2Sem, pSemHyp->pWords[wordNb].pClasses);
      __Asr2Sem_Free2(hAsr2Sem, pSemHyp->pWords[wordNb].szWord);
    }
    __Asr2Sem_Free2(hAsr2Sem, pSemHyp->pWords);

    /* Free the hypo features. */
    __Asr2Sem_Free2(hAsr2Sem, pSemHyp->pFeatures);

    /* Free the hypo classes. */
    __Asr2Sem_Free2(hAsr2Sem, pSemHyp->pClasses);
  }

  /* Free the hypo array. */
  __Asr2Sem_Free2(hAsr2Sem, hAsr2Sem->aHypos);
  hAsr2Sem->aHypos = NULL;
  hAsr2Sem->nbrHypos = 0;

end:
  return err;
}
/* End of __Asr2Sem_FreeHypotheses. */


/* -------------------------------------------------------------------------+
 | Start of the Public API implementation. (see vocon_ext_asr2sem.h)        |
 + -------------------------------------------------------------------------*/


LH_ERROR a2s_CreateAsr2Sem2(LH_HEAP_INTERFACE* pHeapInterface, void* pHeapInst,
                           LH_OBJECT hSemProc, PASR2SEM2* phAsr2Sem)
{
  LH_ERROR err = LH_OK;
  PASR2SEM2 hAsr2Sem = NULL;

  if((pHeapInterface==NULL)||(pHeapInterface->pcalloc==NULL)||(pHeapInterface->pfree==NULL)) {
    err = LH_E_ARG;
    goto end;
  }
  if((lh_ObjIsNull(hSemProc))||(phAsr2Sem==NULL)){
    err = LH_E_ARG;
    goto end;
  }

  /* create a Asr2Sem instance */
  hAsr2Sem = (PASR2SEM2)pHeapInterface->pcalloc(pHeapInst, 1, sizeof(ASR2SEM2));
  if(! hAsr2Sem){
    err = LH_E_RESOURCE;
    goto end;
  }
  hAsr2Sem->heapInterface = *pHeapInterface;
  hAsr2Sem->pHeapInst = pHeapInst;
  hAsr2Sem->hSemProc = hSemProc;
  hAsr2Sem->aHypos = NULL;
  hAsr2Sem->nbrHypos = 0;
  hAsr2Sem->aFeatures = NULL;
  hAsr2Sem->nbrFeatures = 0;
  /*Get the word classes from the semantic processor*/
  err = lh_SemProcFetchClasses (hAsr2Sem->hSemProc,&hAsr2Sem->pWordClasses, &hAsr2Sem->nbrClasses);
  if (LH_OK != err) goto end;
  /* Fetch the configured features. */
  err = lh_SemProcFetchFeatures(hAsr2Sem->hSemProc, &hAsr2Sem->aFeatures, &hAsr2Sem->nbrFeatures);
  if (LH_OK != err) goto end;

  *phAsr2Sem = hAsr2Sem;

end:
  return err;
}

LH_ERROR a2s_Asr2SemAddNBestResult2(PASR2SEM2 hAsr2Sem,
                                   LH_OBJECT hNBestResult)
{
  LH_ERROR            err = LH_OK;
  LH_BOOL             isNBestResult = LH_FALSE;
  size_t              nbrAsrHypotheses = 0;         /* Number of hypotheses in ASR result. */
  size_t              hypNb = 0;                    /* Loop counter for the hypotheses in the result. */
  LH_HYPOTHESIS*      pAsrHypothesis = NULL;        /* Hypothesis coming from ASR result. */
  void*               pTmp = NULL;                  /* Temporary pointer used in the realloc. */
  /* Check that the NBestResult actually implements the INBestResult interface */
  err = lh_ObjHasInterface(hNBestResult, LH_IID_INBESTRESULT, &isNBestResult);
  if (err != LH_OK) goto end;
  if (!isNBestResult){
    err = LH_E_ARG;
    goto end;
  }

  /* Get the number of ASR hypotheses in this result */
  err = lh_NBestResultGetNbrHypotheses(hNBestResult, &nbrAsrHypotheses);
  if (LH_OK != err) goto end;
  /* Realloc the aHypos array and init to 0's.*/
  pTmp = __Asr2Sem_Calloc2(hAsr2Sem, (hAsr2Sem->nbrHypos+nbrAsrHypotheses), sizeof(LH_SEMPROC_NBESTRESULT_HYPOTHESIS), &err);
  if (LH_OK != err) goto end;
  if (hAsr2Sem->aHypos != NULL){
    memcpy(pTmp, hAsr2Sem->aHypos, hAsr2Sem->nbrHypos * sizeof(LH_SEMPROC_NBESTRESULT_HYPOTHESIS));
    __Asr2Sem_Free2(hAsr2Sem, hAsr2Sem->aHypos);
  }
  hAsr2Sem->aHypos = (LH_SEMPROC_NBESTRESULT_HYPOTHESIS*) pTmp;

  /* Add SemprocNBestResults based on the NBestResults */
  /* Note that the SemProc does not require the hypotheses to be sorted by confidence. */
  for(hypNb=0;hypNb<nbrAsrHypotheses;hypNb++){
    LH_SEMPROC_NBESTRESULT_HYPOTHESIS* pSemHyp;         /* Pointer to the entry in the aHypos array being filled. */
    LH_SEMPROC_NBESTRESULT_WORD*       pSemWord;        /* Word structure of the semproc input. */
    LH_HYPOTHESIS_WORD*                pAsrWord;        /* Word structure of the nbestresult. */
    LH_HYPOTHESIS_WORD*                pCurrentAsrSlotWord;    /* Word structure of the nbestresult for current slot. */
    LH_BOOL                            isFirstSlotWord;     /*To check if it is the first slot tag*/
    size_t                             wordNb;          /* Loop counter for the words in the hypothesis. */
    size_t                             wordSize;        /* Word length including terminating \0. */
    size_t                             actualWordNb=0;  /* Number of the words without slot rule tags*/
    size_t                             wordNbSem;         /* Loop counter for sem*/
    size_t                             nbrWordsInCurrentSlot= 0;
    pSemHyp = &(hAsr2Sem->aHypos[hAsr2Sem->nbrHypos]);

    /* Fetch the ASR hypothesis. */
    err = lh_NBestResultFetchHypothesis(hNBestResult, hypNb, &pAsrHypothesis);
    if (LH_OK != err) goto end;

    /* Set number of words and allocate the array of words. */
    /* Strip off the slots*/
    for(wordNb=0;wordNb<pAsrHypothesis->nbrWords;wordNb++){
      if (!pAsrHypothesis->pWords[wordNb].isSlot) {
        actualWordNb +=1;
      }
    }
    pSemHyp->nbrWords =actualWordNb;
    pSemHyp->pWords = (LH_SEMPROC_NBESTRESULT_WORD*) __Asr2Sem_Calloc2(hAsr2Sem, actualWordNb, sizeof(LH_SEMPROC_NBESTRESULT_WORD), &err);
    if (LH_OK != err) goto end;
    pCurrentAsrSlotWord = NULL;
    isFirstSlotWord = LH_FALSE;
    /* Fill word structures */
    for(wordNb=0,wordNbSem = 0;wordNb<pAsrHypothesis->nbrWords;wordNb++) {
      /* copy word string */
      pAsrWord = pAsrHypothesis->pWords + wordNb;
      pSemWord = pSemHyp->pWords + wordNbSem;
      if (!pAsrWord->isSlot) {
        wordSize = strlen(pAsrWord->szWord)+1;
        pSemWord->szWord = (char*)__Asr2Sem_Calloc2(hAsr2Sem, wordSize, sizeof(char), &err);
        if(LH_OK != err) goto end;
        memcpy(pSemWord->szWord, pAsrWord->szWord, wordSize);
        if (pCurrentAsrSlotWord && isFirstSlotWord) {
          err = __Asr2Sem_FillWordClasses2(hAsr2Sem,pSemWord,pCurrentAsrSlotWord,nbrWordsInCurrentSlot);
          if (err) goto end;
          isFirstSlotWord = LH_FALSE;
        }
        /* set word features */
        err = __Asr2Sem_FillWordFeatures2(hAsr2Sem, pSemWord, pAsrWord);
        if (LH_OK != err) goto end;
        wordNbSem++;
      }
      else {
        size_t idx = 0;
        nbrWordsInCurrentSlot = 0;
        if (!pCurrentAsrSlotWord ) { /*only start this check if we have different slot tag*/
          /*look when this slot tag is closed or if it is closed*/
          pCurrentAsrSlotWord = pAsrWord;
          isFirstSlotWord = LH_TRUE;
          for (idx = wordNb+1; idx<pAsrHypothesis->nbrWords; idx++ ) {
            if (!strcmp(pCurrentAsrSlotWord->szWord,pAsrWord[idx-wordNb].szWord)) {		//modified by lin
              break;
            }
            nbrWordsInCurrentSlot++;
          }
        }
        else { /*if this one is the closing one*/
          if (strcmp(pCurrentAsrSlotWord->szWord,pAsrWord->szWord)) {
            err = LH_E_ARG;
            goto end;
          }
          pCurrentAsrSlotWord = NULL; /*Closing one*/
          isFirstSlotWord = LH_FALSE;
        }
      }

    }

    /* set hypothesis features */
    err =  __Asr2Sem_FillHypoFeatures2(hAsr2Sem, hNBestResult, pSemHyp, pAsrHypothesis);
    if(LH_OK != err) goto end;

    /* set hypothesis classes to empty set. */
    pSemHyp->nbrClasses = 0;
    pSemHyp->pClasses = NULL;

    /* Return the hypothesis. */
    err = lh_NBestResultReturnHypothesis(hNBestResult, pAsrHypothesis);
    if (LH_OK != err) goto end;

    /* Succesfully added a hypothesis. Increase count. */
    hAsr2Sem->nbrHypos += 1;
  }
end:
  return err;
}

LH_ERROR a2s_Asr2SemProcess2(PASR2SEM2 hAsr2Sem)
{
  LH_ERROR err = LH_OK;
  LH_SEMPROC_NBESTRESULT_INTERFACE nBestResultInterface;
  void* pNBestResultInst;

  /* Set the callback functions in the interface. */
  nBestResultInterface.pfGetNbrHypotheses = Asr2Sem_GetNbrHypotheses2;
  nBestResultInterface.pfFetchHypothesis = Asr2Sem_FetchHypothesis2;
  nBestResultInterface.pfReturnHypothesis = Asr2Sem_ReturnHypothesis2;

  /* The nbest result instance is the Asr2Sem structure */
  pNBestResultInst = (void*)hAsr2Sem;

  /* Do the actual processing call to semproc. */
  err = lh_SemProcProcess(hAsr2Sem->hSemProc, &nBestResultInterface, pNBestResultInst);
  if (LH_OK != err) goto end;

  /* Free the copied Semproc input. */
  err = __Asr2Sem_FreeHypotheses2(hAsr2Sem);
  if (LH_OK != err) goto end;

end:
  return err;
}

LH_ERROR a2s_Asr2SemClose2(PASR2SEM2* phAsr2Sem)
{
  LH_ERROR err = LH_OK;

  if((phAsr2Sem==NULL)||(*phAsr2Sem==NULL)) {
    err = LH_E_ARG;
    goto end;
  }

  if(lh_ObjIsNull((*phAsr2Sem)->hSemProc)){
    /* The SemProc object has been closed before the Asr2Sem adapter. */
    err = LH_E_ARG;
    goto end;
  }
  /*Return the word classes*/
  err = lh_SemProcReturnClasses ((*phAsr2Sem)->hSemProc, (*phAsr2Sem)->pWordClasses);
  if (LH_OK != err) goto end;
  /* Return the semproc features. */
  err = lh_SemProcReturnFeatures((*phAsr2Sem)->hSemProc, (*phAsr2Sem)->aFeatures);
  if (LH_OK != err) goto end;

  __Asr2Sem_Free2(*phAsr2Sem, *phAsr2Sem);
  *phAsr2Sem = NULL;

end:
  return err;
}


